/***********************************************************
 * Matlab Printer Library
 * Programmers:
 *		Cory Frey
 *		Kendra Konopka
 *		Charles Leichner
 *		Andrew Weatherly
 *
 * Date:
 *		6 December 2010
 *
 * Description:
 *		This library includes functions that can solve various
 *		matrix type problems. These include:
 *			Multiplying by a Scalar
 *			Adding two matrices
 *			Subtracting two matrices
 *			Negating a matrix
 *			Comparing two matrices
 *			Multiplying two matrices
 *			Raising a matrix to a power
 *			Calculating the determinant of a matrix
 *			Calculating the inverse of a matrix
 *			Calculating the eigen values of a matrix
 *			Calculating the eigen vectors of a matrix
 *			Calculating the echelon form of a matrix
 *			Calculating the LU factorization of a matrix
 *			Calculating the QR factorization of a matrix
 *			Calculating the transpose of a matrix
 *			Calculating the trace of a matrix
 *			Calculating the reduced row echelon form of a matrix
 *			Augmenting two matrices
 *			Creating an identity matrix
 *			
 ***********************************************************/
#include <iostream>
#include <vector>
#include <fstream>

class Elements:public std::vector<std::vector<double> >{};
class EigenVals:public std::vector<double>{
	public:
	friend std::ostream& operator<<(std::ostream& out, const EigenVals& Values){
		for(unsigned int i = 0; i< Values.size(); i++ ){
			out << Values.at(i) <<"\t";
		}
		return out;
	}
};

class Matrix {
public:
	// Constructors
	Matrix();
	Matrix(std::string input);
	Matrix(int rows, int columns);
	Matrix( Elements );

	// Destructor
	virtual ~Matrix();
    
    // Copy Constructor
	Matrix( const Matrix& other);
    
	// Assignment Operator 
	Matrix& operator=( const Matrix& other );

	// Getters for Matlab printer
	int getNumRows(){ return numRows; }
	int getNumCols(){ return numCols; }
	std::string getName(){ return name; }

	//Access with []
    std::vector<double>& operator[](const int index);

    // Access operator. use: matrix(Col, Row)
    double& operator()(const int nRow, const int nCol);  
    // << operator
	friend std::ostream& operator<<(std::ostream& out, const Matrix rhs);

	// Scalar mult
	friend Matrix operator*(Matrix &mat, double Scal);
	friend Matrix operator*(double Scal, Matrix &mat);
	
	// Addition and Subtraction
    friend Matrix operator+(Matrix &lhs, Matrix &rhs);
    friend Matrix operator-(Matrix &lhs, Matrix &rhs);
	
	// Uniary minus
	Matrix operator-();
	
	// Equality
    friend bool operator==(Matrix &lhs, Matrix &rhs);
    friend bool operator!=(Matrix &lhs, Matrix &rhs);
	
	// Matrix mult
	Matrix operator*(const Matrix &mat);
	
	// Matrix power
	Matrix Mpow(int power);	

	// Determinant operator
	double determinant();  

    bool isSquare();

	// Minor
	Matrix chopper(int row, int col); 

	// Inverse operator
	Matrix inverse();

	// Eigen Value
	EigenVals eigenValues();

	// Eigen Vector
	std::vector<Matrix> eigenVectors();

	//Echelon form
	Matrix echelon();

	//Lfacot
	Matrix Lfact();

	//Ufact
	Matrix Ufact();
	
	//Q fact
	Matrix Qfact();
	
	//r fact
	Matrix Rfact();

	// Transpose
	Matrix transpose();

	// Identity
	static Matrix identity(int);

	// Augment
	Matrix augment(Matrix);

	// Trace
	double trace();

	// Reduced Row Echelon form (rref)
	Matrix rref();

	// Creates elements given a 2D array
    template<int m, int n>
    static Elements fromArray(double array[m][n]){
        Elements elems =  Elements();
        elems.reserve(m);
		for (unsigned int i = 0; i < m; i++){
			for (unsigned int j = 0; j < n; j++){
				elems[i].push_back(array[i][j]);
            }
        }
        return elems;
    }

protected:
    Elements *mat;       // Representing our matrix as a two dimensional vector of double
	int numRows;
	int numCols;
	std::string name;
    Elements ParseFile(std::string filename);	
	// zeroMatrix
	// creates a matrix of size m by n full of zeros
    // should probably be static
	Elements zeroMatrix(int,int);

	void addRowMultiple(int row1, int row2, double scale);
};
